package com.alibaba.datax.plugin.rdbms.writer;

import com.alibaba.datax.plugin.rdbms.writer.mysql.polygon.Lseg;
import com.alibaba.datax.plugin.rdbms.writer.mysql.polygon.Point;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MysqlPolygonValueConvertor {

    public static long readLongFromBytesBigEndian(byte[] buf){
        return    (long) (buf[0] & 0xff) << 56
                | (long) (buf[1] & 0xff) << 48
                | (long) (buf[2] & 0xff) << 40
                | (long) (buf[3] & 0xff) << 32
                | (long) (buf[4] & 0xff) << 24
                | (long) (buf[5] & 0xff) << 16
                | (long) (buf[6] & 0xff) << 8
                | (long) (buf[7] & 0xff)
                ;
    }

    public static long readLongFromBytesLittleEndian(byte[] buf){
        return    (long) (buf[7] & 0xff) << 56
                | (long) (buf[6] & 0xff) << 48
                | (long) (buf[5] & 0xff) << 40
                | (long) (buf[4] & 0xff) << 32
                | (long) (buf[3] & 0xff) << 24
                | (long) (buf[2] & 0xff) << 16
                | (long) (buf[1] & 0xff) << 8
                | (long) (buf[0] & 0xff)
                ;
    }


    public static double readDoubleFromBytes(byte[] buf, boolean bigEndian){
        long longVal = bigEndian ? readLongFromBytesBigEndian(buf)
                : readLongFromBytesLittleEndian(buf);
        return Double.longBitsToDouble(longVal);
    }


    public static double readDoubleFromBytes(byte[] buf, int offset, boolean bigEndian){
        byte[] bufOf8Bytes = Arrays.copyOfRange(buf,offset,offset+8);
        return readDoubleFromBytes(bufOf8Bytes,bigEndian);
    }

    public static Point readPointFromWkbBytes(byte[] wkbBytes){
        Point point = new Point();

        boolean isBigEndian = false;  // readIsWkbBigEndianByteOrder(wkbBytes[0]);
        double x = readDoubleFromBytes(wkbBytes,9, isBigEndian);
        double y = readDoubleFromBytes(wkbBytes,17, isBigEndian);

        point.setX(x);
        point.setY(y);

        return point;
    }

    public static boolean readIsWkbBigEndianByteOrder(byte b){
        final byte BIG_ENDIAN = 0;
        final byte LITTLE_ENDIAN = 1;
        return b == BIG_ENDIAN;
    }


    public static List<Point> bytesToPoints(byte[] bytes){
        if(bytes == null){
            return null;
        }
        if(bytes.length == 25){
            return Arrays.asList(bytesToOnePoint(bytes));
        }
        return bytesToMutiPoints(bytes);
    }

    private static double bytes2Double(byte[] bytes,int start){
        long value = 0;
        for(int i = 0; i < 8; i++){
            value |= ((long)(bytes[start+i] & 0xff)) << (8 * i);
        }
        return Double.longBitsToDouble(value);
    }

    private static Point bytesToOnePoint(byte[] bytes) {
        Point point = new Point();
        point.setX(bytes2Double(bytes,9));
        point.setY(bytes2Double(bytes,17));
        return point;
    }

    private static List<Point> bytesToMutiPoints(byte[] bytes) {
        int len = (bytes.length - 13)/8;


        List<Point> res = new ArrayList<>();
//        List<Double> doubleList = new ArrayList<>();
        for(int i = 0; i < len/2 ;  ++i){
            double x = bytes2Double(bytes,13+i*2*8);
            double y = bytes2Double(bytes,13+(i*2+1)*8);
            Point point = new Point();
            point.setX(x);
            point.setY(y);
            res.add(point);
//            doubleList.add(bytes2Double(bytes,13+i*8));
        }

//        System.out.println("doubleList ==> "+doubleList);

        return res;
    }


    public static Lseg readLsegFromWkbBytes(byte[] bytes){
        List<Point> pointList = bytesToMutiPoints(bytes);
        Lseg lseg = new Lseg();

        if(pointList == null || pointList.size() < 2){
            throw new RuntimeException("Can't read less than 2 point .");
        }

        lseg.setStartPoint(pointList.get(0));
        lseg.setEndPoint(pointList.get(1));

        return lseg;
    }








}
